﻿using System.Windows.Forms;
using Fawcett.Plugins;
using System.Timers;
using System;
using FSUIPC;
using System.Collections.Generic;
using System.Configuration;
using NCalc;
using System.ComponentModel;

namespace Fawcett.Plugins.FSUIPCX
{
    public class ListOffsets
    {
        /// <summary>
        /// Private Data Stores
        /// </summary>
        private Type _type;
        private Offset<Byte> oByte;
        private Offset<int> oInt;
        private Offset<float> oFloat;
        private Offset<long> oLong;
        private String _name;
        private String _calculate;
        private float _last = -11111111;
        private String _address;
     
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        /// <param name="type"></param>
        /// <param name="address"></param>
        /// <param name="calculate"></param>
        public ListOffsets(string name, string type, string address , string calculate )
        {
             intListOffsets(name, type, address , calculate);
        }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="OffSet"></param>
        public ListOffsets(IOffset OffSet)
        {
            // TODO: Complete member initialization


            intListOffsets(OffSet.Name, OffSet.Type, OffSet.Address , OffSet.Calculate);
        }
        /// <summary>
        /// SetValue
        /// </summary>
        /// <param name="pValue"></param>
        internal void SetValue<T>(T pValue) 
        {
            if (_type == typeof(Byte)) oByte.Value = Convert.ToByte(pValue);
            if (_type == typeof(int)) oInt.Value = Convert.ToInt32(pValue);
            if (_type == typeof(float)) oFloat.Value = Convert.ToSingle(pValue);
            if (_type == typeof(long)) oLong.Value = Convert.ToInt64(pValue);

            Console.WriteLine("{0} type {1} set to {2} ", this.Name, this.DataType , pValue);
        }


        /// <summary>
        /// intListOffsets
        /// </summary>
        /// <param name="name"></param>
        /// <param name="type"></param>
        /// <param name="address"></param>
        /// <param name="calculate"></param>
        internal void intListOffsets(string name, string type, string address, string calculate)
        {
            _type = Type.GetType(type);
            
            _name = name;

            _address = address;

            string str = address.Replace("0x", "");
            int nbr = Int32.Parse(str, System.Globalization.NumberStyles.AllowHexSpecifier);

            if (_type == typeof(Byte)) oByte = new Offset<byte>("PERIODIC", nbr);
            if (_type == typeof(int)) oInt = new Offset<int>("PERIODIC", nbr);
            if (_type == typeof(float)) oFloat = new Offset<float>("PERIODIC", nbr);
            if (_type == typeof(long)) oLong = new Offset<long>("PERIODIC", nbr);

            _calculate = calculate;
        }
        /// <summary>
        /// GetValue
        /// </summary>
        /// <returns></returns>
        internal float GetValue()
        {
            if( _type == typeof(float) ) return oFloat.Value;

            if (_type == typeof(int)) return Convert.ToSingle(oInt.Value );

            if (_type == typeof(long)) return  Convert.ToSingle( oLong.Value );

            if (_type == typeof(Byte)) return Convert.ToSingle(oByte.Value);

            return 0;
        }
        /// <summary>
        /// Calculation
        /// </summary>
        /// <returns></returns>
        internal float Calculation()
        {
            Expression e = new Expression( _calculate  );

            if (_type == typeof(float)) e.Parameters["IN"] =   oFloat.Value;

            if (_type == typeof(Byte)) e.Parameters["IN"] = Convert.ToSingle(oByte.Value);

            if (_type == typeof(int)) e.Parameters["IN"] = Convert.ToSingle(oInt.Value);

            if (_type == typeof(long)) e.Parameters["IN"] = Convert.ToSingle(oLong.Value);

            float rval = (float )e.Evaluate();

            return rval;
        }
        /// <summary>
        /// Public Fields
        /// </summary>
        public String Name { get { return _name; } }
        public String Address { get { return _address; } }

        public String Algorytham { get { return _calculate; } }

        public String DataType { get { return _type.ToString(); } }

        public String RawValue { get { return GetValue().ToString(); } }

        public String Calculated { get { return Calculation().ToString(); } }
        
        /// <summary>
        /// Public Fields that are not visible to DataGrid
        /// </summary>
        [Browsable(false)] 
        public bool hasChanged
        {
            get
            {
                if (Calculation() != _last)
                {
                    _last = Calculation();
                    return true;
                }
                return false;
            }
        }

    }

    /// <summary>
    /// This class implements the IPlugin interface
    /// to provide the ability to parse and isolate email addresses found within
    /// the current editor's text
    /// </summary>
    public class FSUIPCPlugin : CPlugin
    {
        private static System.Timers.Timer aTimer;
        private RibbonButton btnConnect = new System.Windows.Forms.RibbonButton();
        private RibbonButton btnRelease = new System.Windows.Forms.RibbonButton();
        private RibbonButton btnList = new System.Windows.Forms.RibbonButton();
        

        private List<ListOffsets> Offsets = new List<ListOffsets>();


        public bool _Connected = false;
        private bool Connected 
        {
            get { return _Connected; }
            set
            { 
                _Connected = value;
                if( Connected )
                {
                    btnConnect.Enabled = false;
                  btnRelease.Enabled = true;                  
                }
                else 
                {
                    btnConnect.Enabled = true;
                    btnRelease.Enabled = false;  
                }
            }
        }

        public FSUIPCPlugin()
        {
            aTimer = new System.Timers.Timer(1000);
            aTimer.Elapsed += OnTimedEvent;
            aTimer.Enabled = true;

            Offsets.Add(new ListOffsets("PULSE"                  , "System.Int32"  , "0x02B8" , "IN" ));
        }

        private void OnTimedEvent(Object source, ElapsedEventArgs e)
        {
            Process();
        }

        public void Connect(Object sender,
                           EventArgs f)
        {


            if (Connected) return;
            try
            {
                FSUIPC.FSUIPCConnection.Open();
                Connected = true;
            }
            catch (FSUIPCException e)
            {
                if (e.FSUIPCErrorCode == FSUIPCError.FSUIPC_ERR_OPEN)
                {
                    Connected = true;
                    return;
                }

                GlobalEvent.FireEvent(this, new EPlugin(PluginCode.ERROR, e.Message));
                Connected = false;
            }

            
        }

        public override RibbonTab SetupMenu( RibbonTab HomeTab)
        {
            RibbonTab NewTab = new System.Windows.Forms.RibbonTab(Name);
            RibbonPanel AddonPanel = new RibbonPanel("Connection"); ;
            RibbonPanel AddonPane2 = new RibbonPanel("Offsets"); ;

            btnConnect.Image = Resources.Connect.ToBitmap();
            btnRelease.Image = Resources.Connect.ToBitmap();
            btnList.Image = ListForm<List<ListOffsets>>.Image;

            btnConnect.Text = "Connect";
            btnConnect.Click += new EventHandler(this.Connect);
        
            btnRelease.Text = "Disconnect";
            btnRelease.Click += new EventHandler(this.Disconnect);

            btnList.Text = "List";
            btnList.Click += new EventHandler(this.List);

            AddonPanel.ButtonMoreEnabled = false;
            AddonPanel.ButtonMoreVisible = false;

            AddonPane2.ButtonMoreEnabled = false;
            AddonPane2.ButtonMoreVisible = false;

            AddonPanel.Items.Add(btnConnect);
            AddonPanel.Items.Add(btnRelease);

            AddonPane2.Items.Add(btnList);

            NewTab.Panels.Add(AddonPanel);
            NewTab.Panels.Add(AddonPane2);

            Connected = false;

            loadOffsets();

            return NewTab;
        }

        private void loadOffsets()
        {
            OffsetCollection offsets = (OffsetCollection)ConfigurationManager.GetSection("Offsets");
            if (offsets != null)
            {
                foreach (var OffSet in offsets)
                {
                     Offsets.Add(new ListOffsets(OffSet));
                }
            }
        }

        private void List(object sender, EventArgs e)
        {
            ListForm<List<ListOffsets>> nf = new ListForm<List<ListOffsets>>("Offset List" , Offsets);

            if (nf.MdiParent == null) nf.MdiParent = this.parent;

            nf.Show();
        }

        public void Disconnect(Object sender,
                           EventArgs fe)
        {
            if (!Connected) return;
            try
            {
                FSUIPC.FSUIPCConnection.Close();
                Connected = false;
            }
            catch (FSUIPCException e)
            {
                GlobalEvent.FireEvent(this, new EPlugin(PluginCode.ERROR, e.Message));
                Connected = false;
            }
        }

        private int tick = 1;
        public void Process()
        {
            // GlobalEvent.FireEvent(this, new EPlugin(PluginCode.FROM_FSX, "FSX_APU_GEN_ACTIVE", tick));

            tick++;

            if (tick > 100) tick = 1;

            if (! Connected) return;
            try
            {
                FSUIPC.FSUIPCConnection.Process("PERIODIC");
                foreach( ListOffsets off in Offsets )
                {
                    if ( off.hasChanged)
                    {
                        Console.WriteLine("OFFSET {0} has Changed", off.Name);
                        GlobalEvent.FireEvent(this, new EPlugin(PluginCode.FROM_FSX, off.Name, off.Calculated));
                    }
                }
            }

            catch (FSUIPCException e)
            {
                GlobalEvent.FireEvent(this, new EPlugin(PluginCode.ERROR, e.Message));
                Connected = false;
            }
            catch (System.Exception e)
            {
                GlobalEvent.FireEvent(this, new EPlugin(PluginCode.ERROR, e.Message));
                Connected = false;
            }
        }
        public override void PluginEventHandler(object sender, EPlugin p)
        {
            if (p.GetCode == PluginCode.TO_FSX)
            {             
                WriteFSX( p.GetOffset,  p.GetValue );
            } 
           
        }

        private void WriteFSX(string pOffset,int pValue)
        {
            bool found = false; 
            foreach(ListOffsets off in Offsets )
            {
                if (off.Name == pOffset)
                {
                    off.SetValue(pValue);
                    found = true;
                    break;
                }
            }

            if (!found)
            {
                string Message = string.Format("Offset Not found ({0})", pOffset) ;

                GlobalEvent.FireEvent(this, new EPlugin(PluginCode.ERROR, Message));
            }
        }

        private void WriteFSX(string pOffset, float pValue)
        {
            bool found = false;
            foreach (ListOffsets off in Offsets)
            {
                if (off.Name == pOffset)
                {
                    off.SetValue(pValue);
                    found = true;
                    break;
                }
            }

            if (!found)
            {
                string Message = string.Format("Offset Not found ({0})", pOffset);

                GlobalEvent.FireEvent(this, new EPlugin(PluginCode.ERROR, Message));
            }
        }
    }
}